import time
import sys
import network
import ir8266
import machine
import config
import wol
import gc
import lcd1602
from umqtt.simple import MQTTClient

def ir_send_msg(pin, msg):
   
    data = list(map(lambda i:int.from_bytes(msg[2*i:2*i+2], 'little'), range(len(msg)//2)))
    ir8266.send_raw(pin, data)

def wakeup(mac):
    global broadcast_ip
    if len(mac) != 6:
        return
    str_mac = ':'.join(list(map(lambda x:'%.02x'%x, mac)))
    wol.wakeup(str_mac, broadcast_ip)
    
def switch_on(pin):
    pin.on()
    time.sleep(0.5)
    pin.off()
    
def switch_off(pin):
    pin.on()
    time.sleep(0.5)
    pin.off()
        
def sub_cb(topic, msg):
    global portmap, mqttclient, lcd
    print(topic, time.localtime())

    if topic == b'action/exit/' + config.controllerid:
        sys.exit(0)
        
    elif topic == b'action/echo/' + config.controllerid:
        print('echo', msg)
        mqttclient.publish('status/echo', config.controllerid)

    elif topic == b'action/on/' + config.controllerid + b'/lcd':
        lcd.backlight(1)
        lcd.clear()
        lcd.puts(msg.decode(), 0, 0)
        mqttclient.publish('status/on', config.controllerid + b'/lcd')

    elif topic == b'action/off/' + config.controllerid + b'/lcd':
        lcd.backlight(0)
        lcd.clear()
        mqttclient.publish('status/off', config.controllerid + b'/lcd')

    elif topic == b'action/reset/' + config.controllerid:
        machine.reset()
        
    elif topic == b'action/test/' + config.controllerid:
        #assert False
        raise Exception("TestException")
      
    elif topic == b'action/blink/' + config.controllerid:
        pin = machine.Pin(2, machine.Pin.OUT)
        pin.off()
        time.sleep(0.5)
        pin.on()
      
    elif topic.startswith(b'action/on/' + config.controllerid + b'/wol'):
        wakeup(msg)
        mac_addr = b''.join(list(map(lambda b:b'%02x'%msg[b], range(len(msg)))))
        mqttclient.publish('status/on', config.controllerid + b'/wol/' + mac_addr)

    else:
        for devicename, deviceport in config.devices:

            if devicename.startswith(b'ir/'):
                if topic == b'action/on/' + config.controllerid + b'/' + devicename:
                    pin = machine.Pin(deviceport, machine.Pin.OUT)
                    ir_send_msg(pin, msg)
                    mqttclient.publish(b'status/on', config.controllerid + b'/' + devicename)
                elif topic == b'action/off/' + config.controllerid + b'/' + devicename:
                    pin = machine.Pin(deviceport, machine.Pin.OUT)
                    ir_send_msg(pin, msg)
                    mqttclient.publish(b'status/off', config.controllerid + b'/' + devicename)
                else:
                    pass
            elif devicename.startswith(b'switch/'):
                if topic == b'action/on/' + config.controllerid + b'/' + devicename:
                    pin = machine.Pin(deviceport, machine.Pin.OUT)
                    switch_on(pin)
                    mqttclient.publish(b'status/on', config.controllerid + b'/' + devicename) 
                elif topic == b'action/off/' + config.controllerid + b'/' + devicename:
                    pin = machine.Pin(deviceport, machine.Pin.OUT)
                    switch_off(pin)
                    mqttclient.publish(b'status/off', config.controllerid + b'/' + devicename) 
                else:
                    pass
    
def getBroadcastAddr(host, mask):
    ipstr2ip = lambda ipaddr:list(map(lambda x:int(x), ipaddr.split('.')))
    host = ipstr2ip(host)
    mask = ipstr2ip(mask)
    return '.'.join(list(map(lambda i:str(host[i] & mask[i] | ~mask[i] & 0xff), range(4))))

def main():
    global mqttclient, wlan_sta, host_ip, broadcast_ip, lcd
    print('Controller Id: ', config.controllerid)
    wlan_sta = network.WLAN(network.STA_IF)
    ifconfig = wlan_sta.ifconfig()
    host_ip = ifconfig[0]
    host_mask = ifconfig[1]
    broadcast_ip = getBroadcastAddr(host_ip, host_mask)
    print('host ip: ', host_ip)
    print('host mask: ', host_mask)
    print('broadcast ip: ', broadcast_ip)
    i2c = machine.I2C(machine.Pin(12),machine.Pin(14))
    lcd = lcd1602.LCD(i2c, 0x27)

    while True:
        try:
            mqttclient =  MQTTClient(config.cliendid, config.mqttserver, keepalive = 60)
            mqttclient.DEBUG = True
            print('system start', time.localtime())

            mqttclient.connect()
            mqttclient.set_callback(sub_cb)
            mqttclient.subscribe(b'action/+/' + config.controllerid + b'/#')
            mqttclient.publish(b'status/startup/' + config.controllerid, 'host_ip: %s'%host_ip)
            time1 = time.ticks_ms()
            while True:
                mqttclient.check_msg()
                time2 = time.ticks_ms()
                if time2 - time1 >= mqttclient.keepalive * 1000:
                    time1 = time1 + mqttclient.keepalive * 1000
                    mqttclient.ping()
                    print('ping', time.localtime())
        except (KeyboardInterrupt, SystemExit):
            print('Application Exit!')
            break

        except Exception as e:
            print(e.__class__.__name__, ':', e)
    
if __name__ == "__main__":

    main()